/**
 * pajk_android_hybrid.js
 *
 * Created by Jayson on 16/9/20.
 *
 */
(function () {
	var inc = 1;
	var PROTOCOL = 'hybrid';
	var CALLBACK_PREFIX = 'callbackId_';
	var CALLBACKID = 'callBackId';
	var versionCode = null;

	window.hybrid = {
		callBacks: {},
		hybridEvents: {},
		registeredEvents: {},
		getId: function () {
			return Math.floor(Math.random() * 2e9) + '' + inc++;
		},
		buildParam: function (obj) {
			if (obj && typeof obj === 'object') {
				return JSON.stringify(obj);
			} else {
				return obj || '';
			}
		},
		parseData: function (str) {
			var rst;
			if (str && typeof str === 'string') {
				try {
					rst = JSON.parse(str);
				} catch (e) {
					rst = {
						status: {
							code: -140,
							msg: 'PARAM_PARSE_ERROR'
						}
					};
				}
			} else {
				rst = str || {};
			}
			return rst;
		},
		isEmptyObject: function (obj) {
			var o;
			for (o in obj)
				return false;
			return true;
		},
		registerCall: function (id, successCall, errorCall) {
			window.hybrid.callBacks[CALLBACK_PREFIX + id] = {success: successCall, fail: errorCall};
		},
		unregisterCall: function (id) {
			var callbackId = CALLBACK_PREFIX + id;
			var call = {};
			call.success = window.hybrid.callBacks[callbackId] && window.hybrid.callBacks[callbackId].success;
			call.fail = window.hybrid.callBacks[callbackId] && window.hybrid.callBacks[callbackId].fail;
			delete window.hybrid.callBacks[callbackId];
			return call;
		},
		invokeClick: function (element) {
			if (element.click) {//判断是否支持click() 事件
				element.click();
			} else {
				var evt = document.createEvent("MouseEvents"); //创建click() 事件
				evt.initEvent("click", true, true); //初始化click() 事件
				element.dispatchEvent(evt); //分发click() 事件
			}
		},
		/**
		 * 从ua中获取android 版本号
		 * @returns {*}
		 */
		getAndroidVersionCode: function () {
			//get versionString from ua
			if (versionCode === null) {
				var versionString;
				var ua = decodeURIComponent(window.navigator.userAgent.toLowerCase());
				window.hybrid.logInfo("ua: " + ua);
				if (ua.match(/android\s\d+\.\d+\.\d+/)) {
					versionString = ua.match(/android\s\d+\.\d+\.\d+/)[0];
				} else if (ua.match(/android\s\d\.\d/)) {
					versionString = ua.match(/android\s\d\.\d/)[0];
				} else {
					versionString = "";
				}
				//get versionCode from versionString
				if (versionString.length === 0 || versionString.substring(8).length === 0) {
					versionCode = 0;
				} else {
					//"android ".length
                    var subStr = versionString.substring(8);
                    var length = subStr.length;
					//不足5位补足至5位
					if (subStr.length < 5) {
						for (var i = 0; i < (5 - length); i++) {
							subStr += "0";
						}
					}
					//android版本号格式为x.x/x.x.x
					var resultCode = parseInt(subStr.replace(/\./, "0").replace(/\./, "0"));
					if (isNaN(resultCode)) {
						versionCode = -1;
					} else {
						versionCode = resultCode;
					}
				}
			}
			return versionCode;
		},
		/**
		 * 判断是否需要延时发送scheme
		 * desc:v0.1.5版本新增判断android 4.4以下需要增加延时，为了解决native触发js方法，在js方法中直接触发scheme丢失的问题（返回键逻辑）
		 * 
		 * 接入手环项目发现，高版本增加延时会导致丢失，低版本不加延时会导致丢失（坑!）
		 * 因为4.4以上是大部分用户群体，所以这里默认走不延时逻辑
		 */
		isNeedDelay: function () {
			try {
				var code = window.hybrid.getAndroidVersionCode();
				window.hybrid.logInfo("vc: " + code);
				if (code >=0 && code < 40400) {
					return true;
				}
				return false;
			} catch (e) {
				window.hybrid.logInfo(e);
				return false;
			}
		},
		loadUrl: function (url, id) {
			window.hybrid.logInfo("id: " + id + ", url: " + url);
			var a = document.getElementById(PROTOCOL);
			if (a === null) {
				a = document.createElement('a');
				a.setAttribute('target', '_blank');
				a.setAttribute('id', PROTOCOL);
				document.body.appendChild(a);
				window.hybrid.logInfo("a is null");
			}
			a.setAttribute('href', url);
			if (window.hybrid.isNeedDelay()) {
				setTimeout(function () {
					window.hybrid.invokeClick(a);
				},10)
			} else {
				window.hybrid.invokeClick(a);
			}
		},
		/**
		 * scheme回调方法
		 * @param id
		 * @param data jsonObject
		 * @param code
		 */
		onNativeCallBack: function (id, data, code) {
			window.hybrid.logInfo("id: " + id + ", onNativeCallBack params: " + JSON.stringify(data) + ", code: " + code);
			var callObj = window.hybrid.unregisterCall(id);
			var successCall = callObj.success;
			var errorCall = callObj.fail;
			switch (code) {
				//success
				case 1:
					if (successCall) {
						successCall(data);
						window.hybrid.logInfo("id: " + id + ", successCall is called");
					} else {
						window.hybrid.logInfo("id: " + id + ", successCall is not called");
					}
					break;
				//error
				default:
					if (errorCall) {
						errorCall(data);
						window.hybrid.logInfo("id: " + id + ", errorCall is called");
					} else {
						window.hybrid.logInfo("id: " + id + ", errorCall is not called");
					}
					break;
			}
		},
		/**
		 * event回调方法
		 * @param name
		 * @param params
		 */
		onEventCallBack: function (name, params) {
			window.hybrid.logInfo("onEventCallBack name=" + name + " ,params" + JSON.stringify(params));
			var ce = window.hybrid.registeredEvents[name] || [];
			if(ce.length <= 0){
				window.hybrid.logInfo("onEventCallBack ce length <= 0 name=" + name + " ,params" + JSON.stringify(params));
			}
			var eventKeys = Object.keys(ce);
			for (var i = 0; i < eventKeys.length; i++) {
				var eventKey = eventKeys[i];
				if (ce[eventKey]) {
					ce[eventKey](params);
					window.hybrid.logInfo("onEventCallBack called name=" + name + " ,params" + JSON.stringify(params));
				} else {
					window.hybrid.logInfo("onEventCallBack failed name=" + name + " ,params" + JSON.stringify(params));
				}
			}
		},
		logInfo: function (msg) {
			// console.info(msg);
		},
		mockDeviceReady: function () {
			window.hybrid.logInfo("mock deviceready start");
			var evtDeviceReady = document.createEvent('Events');
			evtDeviceReady.initEvent('deviceready', false, false);
			document.dispatchEvent(evtDeviceReady);
			window.hybrid.logInfo("mock deviceready end");
			window.hybrid.hybridEvents.deviceready = evtDeviceReady;
		}
	};
	var hybrid = window.hybrid;

	Object.defineProperties(window, {
		'pajkPostMessage': {
			value: function (successCall, errorCall, params) {
				if (typeof successCall !== 'function') {
					successCall = null;
				}
				if (typeof errorCall !== 'function') {
					errorCall = null;
				}
				//cordova必须需要callBackId
				var id = hybrid.getId();
				if (successCall != null || errorCall != null) {
					hybrid.registerCall(id, successCall, errorCall);
				}
				//脏值校验
				if (!params || !(typeof params === 'object')) {
					params = params || {};
				}
				params.callBackId = id;
				var url = "pajk://" + PROTOCOL + "?content=" + encodeURIComponent(hybrid.buildParam(params));
				hybrid.loadUrl(url, id);
			},
			writable: false,
		},
		'pajkDispatchScheme': {
			value: function (successCall, errorCall, params, scheme) {
				//get schemeTag from userAgent
				var scheme_protocol = 'pajk';
				if (navigator.userAgent.toLowerCase().match(/\s\w+appversion\/\d+/)) {
					var appinfo = navigator.userAgent.toLowerCase().match(/\w+appversion\/\d+/)[0];
					scheme_protocol = appinfo.substring(0, appinfo.indexOf('appversion'));
				}

				if (typeof successCall !== 'function') {
					successCall = null;
				}
				if (typeof errorCall !== 'function') {
					errorCall = null;
				}
				var id = null;
				if (successCall != null || errorCall != null) {
					id = hybrid.getId();
					hybrid.registerCall(id, successCall, errorCall);
				}
				//脏值校验
				if (!params || !(typeof params === 'object')) {
					params = params || {};
				}
				var url = scheme_protocol + "://" + scheme + "?";
				if (!hybrid.isEmptyObject(params)) {
					url += 'content=' + encodeURIComponent(hybrid.buildParam(params));
				}
				if (id != null) {
					url += '&' + CALLBACKID + '=' + id;
				}
				hybrid.loadUrl(url, id);
			},
			writable: false,
		},
		'pajkRegisterEvent': {
			value: function (name, callbackFunction) {
				if (name && (typeof callbackFunction === 'function')) {
					var re = hybrid.registeredEvents[name];
					if (re) {
						if (-1 === re.indexOf(callbackFunction)) {
							hybrid.logInfo("pajkRegisterEvent exist event add: name=" + name + ",callbackFunction=" + callbackFunction);
							re.push(callbackFunction);
						} else {
							hybrid.logInfo("pajkRegisterEvent exist event repeat: name=" + name + ",callbackFunction=" + callbackFunction);
						}
					} else {
						re = [];
						re.push(callbackFunction);
						hybrid.registeredEvents[name] = re;
						hybrid.logInfo("pajkRegisterEvent not exist event add: name=" + name + ",callbackFunction=" + callbackFunction);
					}
					var eventCallbackId = re.indexOf(callbackFunction);
					return eventCallbackId;
				} else {
					hybrid.logInfo("pajkRegisterEvent failed: name=" + name + ",callbackFunction=" + callbackFunction);
				}
			},
			writable: false,
		},
		'pajkUnRegisterEvent': {
			value: function (name, callBackId) {
				var event = hybrid.registedEvents[name] || [];
				if (event[callBackId]) {
					delete event[callBackId];
					hybrid.logInfo("pajkUnRegisterEvent delete: name=" + name + ",callBackId=" + callBackId);
				} else {
					hybrid.logInfo("pajkUnRegisterEvent failed: name=" + name + ",callBackId=" + callBackId);
				}
			},
			writable: false
		},
		'pajkCallBackMessage': {
			value: function (id, data, code) {
				hybrid.onNativeCallBack(id, data, code);
			},
			writable: false
		},
		'pajkCallBackEvent': {
			value: function (name, params) {
				hybrid.onEventCallBack(name, params);
			},
			writable: false
		},
	});

	//解决线上报错
	if(!window.jsOnMessage){
		hybrid.logInfo('window.jsOnMessage is false');
		window.jsOnMessage = function (data) {
			console.info('hybridjs jsOnMessage:', data)
		}
	}
	if(!window.pajkOnMessage){
		hybrid.logInfo('window.pajkOnMessage is false');
		window.pajkOnMessage = function (data) {
			console.info('hybridjs pajkOnMessage:', data)
		}
	}

	hybrid.addEventListener = document.addEventListener;
	document.addEventListener = function (evt, handler, capture) {
		var e = evt.toLowerCase();
		if (typeof hybrid.hybridEvents[e] != 'undefined') {
			var evtCurrent = hybrid.hybridEvents[e];
			if (evtCurrent.type == 'deviceready') {
				hybrid.logInfo("event deviceready fired, call handler immediately");
				handler.call();
			} else {
				hybrid.logInfo("event deviceready not fired, call origin addEventListener");
				hybrid.addEventListener.call(document, evt, handler, capture);
			}
		} else {
			hybrid.logInfo("hybrid events has no this event, call origin addEventListener");
			hybrid.addEventListener.call(document, evt, handler, capture);
		}
	};
	hybrid.mockDeviceReady();
})();